import * as THREE from "../../../modules/three/three.module.js";
import {FlyOrbitControls} from "../../../modules/three/jsm/controls/FlyOrbitControls.js";
import MainStore, {getJsonData} from "../store/MainStore.js";
import UnrealBloomHelper from "../lib/helpers/UnrealBloomHelper.js";
import GlowHelper from "../lib/helpers/GlowHelper.js";
import Scene3dEvent from "./event/Scene3dEvent.js";
import THREEx from "../util/keyboardState.js";
import {MouseButton} from "../lib/CommonConsts.js";
import {BreakableEmitter} from "../entity/SimpleBreakableEntity.js";
import {ModelBreakableEmitter} from "../entity/ModelBreakableEntity.js";
import {modifyCubeVertShader, modifyEquirectVertShader} from "../util/Utils.js";
import CubeRender from "../lib/env/CubeRender.js";

// modifyEquirectVertShader()
modifyCubeVertShader()
const textureRefract = new THREE.CubeTexture()
textureRefract.mapping = THREE.CubeRefractionMapping

const keyboard = new THREEx.KeyboardState();
const startPos = new THREE.Vector2();
const endPos = new THREE.Vector2();
const getDistanceSq = (p, q) => {
  const dx = q.x - p.x;
  const dy = q.y - p.y;
  return dx * dx + dy * dy;
}

function createSkyBox(url) {
  const cubeGeo = new THREE.SphereGeometry(300000, 128, 128)
  const skyTexture = new THREE.TextureLoader().load(url)
  skyTexture.colorSpace = THREE.LinearSRGBColorSpace
  const cubeBox = new THREE.Mesh(cubeGeo,
    new THREE.MeshBasicMaterial({
      color: '#fff',
      map: skyTexture,
      side: THREE.DoubleSide,
      toneMapped: false,
      // depthTest: false,
    }))
  cubeBox.renderOrder = -1
  cubeBox.rotateX(Math.PI / 2)
  return cubeBox
}

export default class Scene3d {
  /**
   *
   * @type {THREE.Scene}
   */
  scene
  /**
   * 当前使用的相机
   * @type {THREE.PerspectiveCamera | THREE.OrthographicCamera}
   */
  camera
  /**
   * 透视相机
   * @type {THREE.PerspectiveCamera}
   */
  _perspCamera
  /**
   * 正交相机
   * @type {THREE.OrthographicCamera}
   */
  _orthoCamera
  /**
   * @type {THREE.WebGLRenderer}
   */
  renderer
  /**
   * @type {FlyOrbitControls}
   */
  cameraControls
  /**
   * @type {MainStore}
   */
  store
  /**
   * 为设备模型的线框显示发光效果
   * @type {UnrealBloomHelper}
   */
  glowHelper
  outlineHelper
  enableGlowHelper = false
  _useFog = false
  _startFly = true
  /**
   * @type {ModelBreakableEmitter}
   */
  breakableEmitter
  /**
   * @type {CubeRender}
   */
  cubeRender
  cubeRender2

  init(canvas) {
    this._dimension = {w: window.innerWidth, h: window.innerHeight, dom: canvas, sizeComputed: false};
    this.scene = new THREE.Scene();
    this.breakableEmitter = new ModelBreakableEmitter(this.scene)
    const aspect = window.innerWidth / window.innerHeight;
    this._perspCamera = new THREE.PerspectiveCamera(60, aspect, 0.1, 500000); // 1e12
    this._perspCamera.up.set(0, 0, 1);
    this._perspCamera.position.set(52, -328, 122);
    const vs = 2; // vertical size
    const hs = vs * aspect; // horizontal size
    this._orthoCamera = new THREE.OrthographicCamera(-hs, hs, vs, -vs, 0.05, 4000);
    this._orthoCamera.up.set(0, 0, 1);
    this._orthoCamera.position.set(0, 200, 0);
    this.camera = this._perspCamera;
    this.clock = new THREE.Clock();
    this.renderer = new THREE.WebGLRenderer({antialias: true, canvas: canvas});
    this.renderer.setSize(window.innerWidth, window.innerHeight, false);
    this.renderer.setClearColor('#fff', 1);
    this.glowHelper = new UnrealBloomHelper(this.renderer, this.scene, this.camera);
    this.outlineHelper = new GlowHelper(this.renderer, this.scene, this.camera);
    this.store = new MainStore();
    this.cubeRender = new CubeRender(this.renderer, this.scene, {near: 1, far: 500})
    this.cubeRender2 = new CubeRender(this.renderer, this.scene, {near: 1, far: 500})
    this.cubeRender2.cubeRenderTarget.texture.mapping = THREE.CubeRefractionMapping

    const cameraControls = new FlyOrbitControls(this.camera, this.renderer.domElement);
    cameraControls.screenSpacePanning = false;
    cameraControls.minDistance = 0.3;
    cameraControls.maxDistance = 5000000;
    cameraControls.enableDamping = true;
    cameraControls.enableKeys = false
    cameraControls.maxPolarAngle = Math.PI * 0.5;
    // cameraControls.enablePan = false
    this.cameraControls = cameraControls;
    cameraControls.enabled = !this._startFly

    this._initLights();
    this._initSky();
  }

  _initLights() {
    const directionalLight = new THREE.DirectionalLight(0xffddcc,2.0);
    directionalLight.position.set(200, 200, 100).normalize();
    this.scene.add(directionalLight);

    const ambientLight = new THREE.AmbientLight(0xffddcc, 1.48)
    this.scene.add(ambientLight);
  }

  // 初始化天空盒
  _initSky() {
    // const innerSphereBox = createSkyBox("../public/3d/pics/sphereBack.jpg")
    // this.scene.attach(innerSphereBox)
    const textureLoader = new THREE.TextureLoader();
    const textureEquirec = textureLoader.load( '../public/3d/pics/sphereBack.jpg')
    textureEquirec.mapping = THREE.EquirectangularReflectionMapping; // EquirectangularReflectionMapping
    textureEquirec.colorSpace = THREE.LinearSRGBColorSpace;
    textureEquirec.rotation = Math.PI / 3
    this.scene.background = textureEquirec

    if (this._useFog) {
      this.scene.fog = new THREE.Fog( "#cce0ff", 330, 5000 );
      this.renderer.setClearColor( this.scene.fog.color );
      const groundMaterial = new THREE.MeshLambertMaterial( { color: '#cce0ff' } );
      const ground = new THREE.Mesh( new THREE.PlaneGeometry( 20000, 20000, 8, 8 ), groundMaterial );
      ground.receiveShadow = true;
      this.scene.add( ground );
    }
  }

  // 调用时机：canvas画布的大小发生变化后
  _updateForDimensionChange() {
    const dom = this._dimension.dom
    if (!dom || this._dimension.sizeComputed) {
      return
    }
    const style = getComputedStyle(dom)
    const width = parseInt(style.width)
    const height = parseInt(style.height)
    if (isNaN(width) || isNaN(height)) {
      return
    }
    if (width) {
      this._dimension.sizeComputed = true
    }
    const {w, h} = this._dimension
    if (w !== width || h !== height) {
      const aspect = width / height
      this._perspCamera.aspect = aspect
      this._perspCamera.updateProjectionMatrix()
      const vs = 2 // vertical size
      const hs = vs * aspect // horizontal size
      this._orthoCamera.left = -hs
      this._orthoCamera.right = hs
      this._orthoCamera.updateProjectionMatrix()
      this.renderer.setDrawingBufferSize(width, height, window.devicePixelRatio)
      this.renderer.setSize(width, height, false)
      this.outlineHelper.onCanvasResize()
    }
    this._dimension.w = width
    this._dimension.h = height
  }

  async loadFromLocal() {
    const data = await getJsonData();
    this.store.storageLoad(data, this);
    this.scene.dispatchEvent(Scene3dEvent.jsonDataLoad);
  }
  focusToPosition(position) {
    const offset = new THREE.Vector3(0, 5, 2);
    this.cameraControls.target.copy(position)
    this.camera.position.copy(position).add(offset)
    this.cameraControls.update()
  }
  /**
   * 设置相机焦点
   * @param {THREE.Vector3} focus
   */
  setCameraFocus(focus) {
    this.cameraControls.target.copy(focus)
    this.cameraControls.update()
  }

  tickAircraft() {
    const aircraft = this.store.aircraft
    if (!aircraft || !aircraft.mixer) {
      return
    }
    aircraft.mixer.update(aircraft.clock.getDelta() * 2)
    aircraft.tick(this.clock, keyboard, this.camera)
    aircraft.tickFire()
  }
  fireBullet() {
    if (!this.store.aircraft) {
      return
    }
    const aircraft = this.store.aircraft
    aircraft.fire()
  }
  /**
   * 渲染单帧画面
   * @param {number?} time requestAnimationFrame传入的从网页加载开始算的毫秒浮点数。默认为window.performance.now()
   */
  render(time) {
    this.renderer.render(this.scene, this.camera);
    // this._updateForDimensionChange();
    this.tickAircraft()
    this.breakableEmitter.tick(this)
    this.renderCubeTexture()
  }
  renderCubeTexture() {
    const aircraft = this.store.aircraft
    if (!aircraft) {
      return
    }
    this.cubeRender.cubeCamera.position.copy(aircraft.position).add(aircraft.dir)
    this.cubeRender2.cubeCamera.position.copy(aircraft.dir).multiplyScalar(3).add(aircraft.position)
    this.cubeRender.renderCube()
    this.cubeRender2.renderCube()
  }
  getCubeTexture(needRefraction) {
    if (needRefraction) {
      // this.cubeRender2.cubeRenderTarget.texture.mapping = THREE.CubeRefractionMapping
      return this.cubeRender2.cubeRenderTarget.texture
    } else {
      return this.cubeRender.cubeRenderTarget.texture
    }
  }

  startRender() {
    const renderOnce = (time) => {
      this.render(time);
      this.rafId = requestAnimationFrame(renderOnce);
    }
    renderOnce(window.performance.now());
  }

  destroy() {
    cancelAnimationFrame(this.rafId);
    // window.removeEventListener('keydown', this.keydownHandler);
    this.scene.clear();
    this.renderer.dispose();
  }

  pointerDown(event){
    const bounds = event.target.getBoundingClientRect();
    startPos.set(event.clientX - bounds.x, event.clientY - bounds.y);
    const aircraft = this.store.aircraft
    if (!aircraft || !aircraft.mixer) {
      return
    }
    aircraft.pointerDown(event, this.camera)
  }
  pointerMove(event){
    const aircraft = this.store.aircraft
    if (!aircraft || !aircraft.mixer) {
      return
    }
    aircraft.pointerMove(event, this.camera)
  }
  pointerUp(event){
    const aircraft = this.store.aircraft
    if (aircraft) {
      aircraft.pointerUp(event, this.camera)
    }
    const bounds = event.target.getBoundingClientRect();
    endPos.x = event.clientX - bounds.x;
    endPos.y = event.clientY - bounds.y;
    const pointerMoved = getDistanceSq(startPos, endPos) > 4;
    const mouseLeftClicked = event.button === MouseButton.left;
    if (!mouseLeftClicked || pointerMoved) {
      return;
    }
    this.fireBullet()
  }
}
